#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <glib.h>
#include <gio/gio.h>
#include <gio/gunixsocketaddress.h>

#include "server.h"

G_DEFINE_TYPE(RServer, r_server, G_TYPE_OBJECT);
G_DEFINE_TYPE(RClient, r_client, G_TYPE_OBJECT);

#define MAXIMUM_REQUEST_LENGTH 65535
#define SERVER_PORT 9998
enum {
	CLPRS_SERVER_CLIENT_CONNECTED,
	CLPRS_SERVER_CLIENT_DISCONNECTED,
	CLPRS_SERVER_LAST_SIGNAL
};
static guint RServer_signals[CLPRS_SERVER_LAST_SIGNAL] = { 0 };

struct RServerPrivate {
	GSocket *socket;
	GIOChannel *channel;
	GHashTable *clients;
};

struct RClientPrivate {
	RServer *server;
	GSocket *socket;
	GIOChannel *channel;
};

enum
{
	CLPRS_CLIENT_DISCONNECTED,
	CLPRS_CLIENT_LAST_SIGNAL
};

static guint r_client_signals[CLPRS_CLIENT_LAST_SIGNAL] = { 0 };

void RClient_disconnect(RClient *client)
{
	g_io_channel_shutdown(client->priv->channel, TRUE, NULL);
}

static void r_client_init(RClient *client)
{
	client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, r_client_get_type (), RClientPrivate);
}

static void r_client_class_init (RClientClass *klass)
{
	g_type_class_add_private(klass, sizeof (RClientPrivate));

	r_client_signals[CLPRS_CLIENT_DISCONNECTED] =
		g_signal_new (CLPRS_CLIENT_SIGNAL_DISCONNECTED,
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET(RClientClass, disconnected),
				NULL, NULL,
				NULL,
				G_TYPE_NONE, 0); 
}

RServer* r_server_new()
{
	RServer *server = g_object_new(r_server_get_type (), NULL);
	return server;
}


static void r_client_disconnected_cb(RClient *client, RServer *server)
{   
	g_signal_handlers_disconnect_matched(client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, server);
	g_hash_table_remove(server->priv->clients, client->priv->channel);
	g_signal_emit(server, RServer_signals[CLPRS_SERVER_CLIENT_DISCONNECTED], 0, client);
}


static gboolean socket_connect_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
{
	RServer *server = data;
	RClient *client;
	GSocket *data_socket;
	GError *error = NULL;

	data_socket = g_socket_accept(server->priv->socket, NULL, &error);
	if (error)
		g_warning ("Error accepting connection: %s", strerror (errno));
	g_clear_error (&error);
	if (!data_socket)
		return FALSE;

	client = g_object_new(r_client_get_type (), NULL);
	client->priv->server = server;
//TCP	g_signal_connect(client, CLPRS_CLIENT_SIGNAL_DISCONNECTED, G_CALLBACK(RClient_disconnected_cb), server);
	client->priv->socket = data_socket;
	client->priv->channel = g_io_channel_unix_new(g_socket_get_fd(data_socket));
	g_hash_table_insert(server->priv->clients, client->priv->channel, client);
	g_signal_emit(server, RServer_signals[CLPRS_SERVER_CLIENT_CONNECTED], 0, client);

	return TRUE;
}

gboolean r_server_start(RServer *server){

	GError *error = NULL;
	GInetAddress *address = NULL;
	GSocketAddress *socket_address = NULL;

	address = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
	socket_address = g_inet_socket_address_new(address, SERVER_PORT);
	if(socket_address == NULL){
		g_error("socket_address NULL");
		return -1;
	}

	server->priv->socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
	if(!server->priv->socket || g_socket_bind(server->priv->socket, socket_address, TRUE, &error)){
		g_warning("Error creating IPV4 socket: %s", error->message);
		g_clear_error (&error);
		g_object_unref(server->priv->socket);
		return FALSE;
	}

	server->priv->channel = g_io_channel_unix_new(g_socket_get_fd(server->priv->socket));

	g_io_add_watch(server->priv->channel, G_IO_IN, socket_connect_cb, server);

	g_object_unref(socket_address);
	return TRUE;
}


gsize r_server_get_n_clients(RServer *server)
{
	return g_hash_table_size(server->priv->clients);
}

static void r_server_init(RServer *server)
{
	server->priv = G_TYPE_INSTANCE_GET_PRIVATE(server, r_server_get_type (), RServerPrivate);
	server->priv->clients = g_hash_table_new_full(g_direct_hash, g_direct_equal, (GDestroyNotify)g_io_channel_unref, g_object_unref);
}

static void r_server_finalize(GObject *object)
{
	RServer *server = (RServer *) object;
//	if (server->priv->socket_path)
//		unlink (server->priv->socket_path);
//	G_OBJECT_CLASS(RServer_parent_class)->finalize (object);
}
static void r_server_class_init(RServerClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS(klass);
	object_class->finalize = r_server_finalize;
	g_type_class_add_private(klass, sizeof(RServerPrivate));
	RServer_signals[CLPRS_SERVER_CLIENT_CONNECTED] =
		g_signal_new(CLPRS_SERVER_SIGNAL_CLIENT_CONNECTED,
				G_TYPE_FROM_CLASS(klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (RServerClass, client_connected),
				NULL, NULL,
				NULL,
				G_TYPE_NONE, 1, r_client_get_type ());
	RServer_signals[CLPRS_SERVER_CLIENT_DISCONNECTED] =
		g_signal_new(CLPRS_SERVER_SIGNAL_CLIENT_DISCONNECTED,
				G_TYPE_FROM_CLASS(klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET(RServerClass, client_disconnected),
				NULL, NULL,
				NULL,
				G_TYPE_NONE, 1, r_client_get_type ());
}


int main(){
	RServer * server =  r_server_new();

	if(!r_server_start(server)){
		g_error("Star server Error");
		return -1;
	}

	return 0;
}
